/** @file

Copyright (c) 2006-2023, ikun BIOS, ikun Technology (Beijing) Co., Ltd.. All
Rights Reserved.

You may not reproduce, distribute, publish, display, perform, modify, adapt,
transmit, broadcast, present, recite, release, license or otherwise exploit
any part of this publication in any form, by any means, without the prior
written permission of ikun Technology (Beijing) Co., Ltd..

**/

#include <Uefi.h>
#include <PowerManage.h>
#include <SetupConfig.h>
#include <Protocol/HiiConfigRouting.h>
#include <Library/FileExplorerLib.h>
#include <Library/ReportStatusCodeLib.h>
#include <Protocol/KlHiiPopup.h>
#include <Protocol/PowerManageDxe.h>

STATIC EFI_CALLBACK_INFO                     *mPowerManageCallBackInfo;
STATIC EFI_HII_CONFIG_ROUTING_PROTOCOL       *gHiiConfigRouting = NULL;
EFI_GUID   mPowerManageFormSetGuid      = FORMSET_ID_GUID_POWER_MANAGE;

/**
  Brief description of GenericRouteConfig.

  @param  This
  @param  Configuration
  @param  Progress

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
EFIAPI
PowerManageRouteConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Configuration,
  OUT EFI_STRING                             *Progress
  )
{
  EFI_STATUS                    Status;
  UINTN                         BufferSize;
 //add-klk-lyang-P000A-start//
  VOID                          *VarPtr;
  SYSTEM_ACCESS                 SystemAccess;
  EFI_GUID                      SysAccessGuid = SYSTEM_ACCESS_GUID;
  EFI_GUID                      *GuidPtr;
  CHAR16                        *VarName;
  UINTN                         StoreBufferSize;
  SYSTEM_SETUP_CONFIGURATION    *SetupConfiguration;
  //SetupSaveCallBackSYSTEM_SETUP_CONFIGURATION    *SetupConfigurationBackup;
//add-klk-lyang-P000A-end//
  EFI_POWER_MANAGE_DXE_PROTOCOL *PowerManageProtocol;
  RTC_WAKE_UP_DATA              StRtcWakeUpData;
  BOOLEAN                       SetupConfigFlag;

  GuidPtr = NULL;
  VarName = NULL;
  SetupConfigFlag = TRUE;
  ZeroMem(&StRtcWakeUpData,sizeof(RTC_WAKE_UP_DATA));
  if (Configuration == NULL || Progress == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  *Progress = Configuration;

  Status = gBS->LocateProtocol(&gEfiPowerManageProtocolGuid, NULL, (VOID **) &PowerManageProtocol);
  if (EFI_ERROR(Status)) {
    return Status;
  }
  //
  // Get Buffer Storage data from EFI variable
  //
  BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
  SetupConfiguration = AllocatePool(BufferSize);
  //SetupConfigurationBackup = AllocatePool(BufferSize);
  Status = gRT->GetVariable (
                  SETUP_VARIABLE_NAME,
                  &gSystemConfigurationGuid,
                  NULL,
                  &BufferSize,
                  SetupConfiguration
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //CopyMem((VOID *)SetupConfigurationBackup, (VOID *)SetupConfiguration, sizeof (SYSTEM_SETUP_CONFIGURATION));
//add-klk-lyang-P000A-start//
  VarPtr = SetupConfiguration;
  GuidPtr = &gSystemConfigurationGuid;
  VarName = SETUP_VARIABLE_NAME;
//add-klk-lyang-P000A-end//
  if (!HiiIsConfigHdrMatch (Configuration, &gSystemConfigurationGuid, SETUP_VARIABLE_NAME)) {
     //add-klk-lyang-P000A-start//
     if(!HiiIsConfigHdrMatch (Configuration, &SysAccessGuid, L"SystemAccess")){
       return EFI_NOT_FOUND;
     } else {
       BufferSize = sizeof (SYSTEM_ACCESS);
       Status = gRT->GetVariable (
                       L"SystemAccess",
                       &SysAccessGuid,
                       NULL,
                       &BufferSize,
                       &(SystemAccess)
                       );
       if (EFI_ERROR (Status)) {
         return EFI_NOT_FOUND;
       }
       VarPtr = &SystemAccess;
       VarName = L"SystemAccess";
       GuidPtr = &SysAccessGuid;
       SetupConfigFlag = FALSE;
     }
 //add-klk-lyang-P000A-end//
  }

  //
  // Check if configuring Name/Value storage
  //
  if (StrStr (Configuration, L"OFFSET") == NULL) {
     return EFI_SUCCESS;
  }
  StoreBufferSize = BufferSize;
  // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()
  //
  Status = gHiiConfigRouting->ConfigToBlock (
                                   gHiiConfigRouting,
                                   Configuration,
                                   (UINT8 *) VarPtr,
                                   &BufferSize,
                                   Progress
                                   );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  if(SetupConfigFlag /*&& (SetupConfiguration->RtcWakeUpPolicy)*/) {
    //if(CompareMem((VOID * )SetupConfiguration, (VOID *) SetupConfigurationBackup, sizeof (SYSTEM_SETUP_CONFIGURATION)!=0)) {
      DEBUG((DEBUG_ERROR, "%a :update rtc wake up,config=%s\n",__FUNCTION__,Configuration));
      StRtcWakeUpData.Year  = SetupConfiguration->WakeUpDate.Year;
      StRtcWakeUpData.Month = SetupConfiguration->WakeUpDate.Month;
      StRtcWakeUpData.Date  = SetupConfiguration->WakeUpDate.Date;
      StRtcWakeUpData.Week  = SetupConfiguration->WakeUpWeek;
      StRtcWakeUpData.Hour  = SetupConfiguration->WakeUpTime.Hour;
      StRtcWakeUpData.Min   = SetupConfiguration->WakeUpTime.Minute;
      StRtcWakeUpData.Sec   = SetupConfiguration->WakeUpTime.Second;
      PowerManageProtocol->UpdateRtcWakeUpData(PowerManageProtocol, SetupConfiguration->RtcWakeUpPolicy, &StRtcWakeUpData);
    //}
  }
  //
  // Store Buffer Storage back to EFI variable
  //
  Status = gRT->SetVariable(
                  VarName,
                  GuidPtr,
                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
                  StoreBufferSize,
                  VarPtr
                  );
  return Status;
}

/**
  Brief description of SyncPowerManageToSystemTime.

  @param  SetupConfiguration

**/
STATIC
EFI_STATUS
SyncPowerManageToSystemTime(
  IN SYSTEM_SETUP_CONFIGURATION            *SetupConfiguration
  )
{
  EFI_STATUS                                Status;
  EFI_TIME                                  EfiTime;

  Status = gRT->GetTime (&EfiTime, NULL);
  if(!EFI_ERROR(Status)) {
    SetupConfiguration->WakeUpDate.Year = EfiTime.Year;
    SetupConfiguration->WakeUpDate.Month = EfiTime.Month;
    SetupConfiguration->WakeUpDate.Date = EfiTime.Day;
    SetupConfiguration->WakeUpTime.Hour = EfiTime.Hour;
    SetupConfiguration->WakeUpTime.Minute = EfiTime.Minute;
    SetupConfiguration->WakeUpTime.Second = EfiTime.Second;
  }
  return Status;
}

/**
  Brief description of PowerManageCallbackRoutine.

  @param  This
  @param  Action
  @param  QuestionId
  @param  Type
  @param  Value
  @param  ActionRequest

**/
EFI_STATUS
EFIAPI
PowerManageCallbackRoutine(
    IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    IN  EFI_BROWSER_ACTION                     Action,
    IN  EFI_QUESTION_ID                        QuestionId,
    IN  UINT8                                  Type,
    IN  EFI_IFR_TYPE_VALUE                     *Value,
    OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
)
{
  EFI_STATUS                            Status;
  EFI_POWER_MANAGE_DXE_PROTOCOL         *PowerManageProtocol;
  SYSTEM_SETUP_CONFIGURATION            *SetupConfiguration;
  UINTN                                 BufferSize;

  Status = EFI_NOT_FOUND;
  if ((Action != EFI_BROWSER_ACTION_CHANGED) /*&&
      (Action != EFI_BROWSER_ACTION_CHANGING)*/
    ){
    return EFI_UNSUPPORTED;
  }
  *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
  Status = gBS->LocateProtocol(&gEfiPowerManageProtocolGuid, NULL, (VOID **) &PowerManageProtocol);
  if (EFI_ERROR(Status)) {
      return Status;
  }
  if(QuestionId == POWER_MANAGE_RTC_WAKEUP_ID){
    BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
    SetupConfiguration = AllocatePool(BufferSize);
    Status = HiiGetBrowserData(
             &gSystemConfigurationGuid,
             SETUP_VARIABLE_NAME,
             BufferSize,
             (UINT8 *)SetupConfiguration
             );
    if (EFI_ERROR (Status)) {
      return EFI_NOT_FOUND;
    }
    //DEBUG ((EFI_D_ERROR,"PowerManageCallbackRoutine LINE:%d,Action=%x,brower=%d,value=%d\n",__LINE__,Action,SetupConfiguration->RtcWakeUpPolicy,Value->u8));
    //if((SetupConfiguration->RtcWakeUpPolicy == 0) && (Value->u8 > 0)) {
      DEBUG ((EFI_D_ERROR,"PowerManageCallbackRoutine: Sync the system time.\n"));
      SyncPowerManageToSystemTime(SetupConfiguration);
      BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
      HiiSetBrowserData (
        &gSystemConfigurationGuid,
        SETUP_VARIABLE_NAME,
        BufferSize,
        (UINT8 *)SetupConfiguration,
        NULL
      );
    //}
  }
  return Status;
}

/**
  Brief description of InstallPowerManageCallbackRoutine.

  @param  DriverHandle
  @param  HiiHandle

**/
EFI_STATUS
InstallPowerManageCallbackRoutine(
    IN EFI_HANDLE                             DriverHandle,
    IN EFI_HII_HANDLE                         HiiHandle
)
{
    EFI_STATUS                                Status;
    EFI_GUID                                  FormsetGuid = FORMSET_ID_GUID_POWER_MANAGE;

    mPowerManageCallBackInfo = AllocatePool(sizeof(EFI_CALLBACK_INFO));
    if (mPowerManageCallBackInfo == NULL) {
        return EFI_OUT_OF_RESOURCES;
    }

    mPowerManageCallBackInfo->Signature                    = EFI_CALLBACK_INFO_SIGNATURE;
    mPowerManageCallBackInfo->DriverCallback.ExtractConfig = GenericExtractConfig;
    mPowerManageCallBackInfo->DriverCallback.RouteConfig   = PowerManageRouteConfig;
    mPowerManageCallBackInfo->DriverCallback.Callback      = PowerManageCallbackRoutine;
    mPowerManageCallBackInfo->HiiHandle                    = HiiHandle;
    CopyGuid(&mPowerManageCallBackInfo->FormsetGuid, &FormsetGuid);

    Status = gBS->LocateProtocol (
                    &gEfiHiiConfigRoutingProtocolGuid,
                    NULL,
                    (VOID **)&gHiiConfigRouting
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
    //
    // Install protocol interface
    //
    Status = gBS->InstallProtocolInterface(
                 &DriverHandle,
                 &gEfiHiiConfigAccessProtocolGuid,
                 EFI_NATIVE_INTERFACE,
                 &mPowerManageCallBackInfo->DriverCallback
             );
    ASSERT_EFI_ERROR(Status);
    return Status;
}
